﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Enviorment;
using System.Threading;
using ILRuntime.Runtime;
using System;
using AppDomain = ILRuntime.Runtime.Enviorment.AppDomain;
using Sirenix.OdinInspector;

namespace Main
{
    [HideMonoScript]
    [RequireComponent(typeof(ComponentIDE))]
    public class HotfixMonoBehaviour : MonoBehaviour
    {
        //AppDomain是ILRuntime的入口，最好是在一个单例类中保存，整个游戏全局就一个，这里为了示例方便，每个例子里面都单独做了一个
        //大家在正式项目中请全局只创建一个AppDomain
        AppDomain appDomain;
        System.IO.MemoryStream fs;
        System.IO.MemoryStream p;
        private object hotfix;     // Hotfix Class
        private IMethod iAwake;
        private IMethod iStart;
        private IMethod iEnable;
        private IMethod iDisable;
        private IMethod iUpdate;
        private IMethod iLateUpdate;
        private IMethod iFixedUpdate;
        private IMethod iOnDestroy;
        /// <summary>
        /// 加载热更程序集
        /// </summary>
        private void LoadHotFixAssembly()
        {
            //首先实例化ILRuntime的AppDomain，AppDomain是一个应用程序域，每个AppDomain都是一个独立的沙盒
            appDomain = new ILRuntime.Runtime.Enviorment.AppDomain(ILRuntimeJITFlags.JITOnDemand);
#if UNITY_EDITOR && AB_Local
            // DLL 文件路径
            string DllPath = PathConfig.CodeDllPath;
            // 加载DLL
            byte[] dll = File.ReadAllBytes(DllPath + "/HotFix.dll.bytes");
            // 加载调试数据库
            byte[] pdb = File.ReadAllBytes(DllPath + "/HotFix.pdb.bytes");
#else
            AssetBundle codeAB = AssetBundlesManager.Instance.LoadAB("code");
            byte[] dll = codeAB.LoadAsset<TextAsset>("Hotfix.dll").bytes;
            byte[] pdb = codeAB.LoadAsset<TextAsset>("Hotfix.pdb").bytes;
            codeAB.Unload(false);
#endif
            fs = new MemoryStream(dll);
            p = new MemoryStream(pdb);
            try
            {
                appDomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
            }
            catch
            {
                Debug.LogError("加载热更DLL失败，请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL");
            }
            InitializeILRuntime();
            OnHotFixLoaded();
        }
        void InitializeILRuntime()
        {
#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
            //由于Unity的Profiler接口只允许在主线程使用，为了避免出异常，需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
            appDomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId;
#endif
            //这里做一些ILRuntime的注册，这个示例暂时没有需要注册的
            DelegateAdapter();
            CrossBindingAdaptor();
        }
        // 注册委托
        private void DelegateAdapter()
        {
            appDomain.DelegateManager.RegisterMethodDelegate<System.Boolean>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Int32>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Single>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.String>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Int64, System.Int64>();
            appDomain.DelegateManager.RegisterMethodDelegate<UnityEngine.AssetBundle>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.String, System.Single>();
            appDomain.DelegateManager.RegisterMethodDelegate<System.Boolean, System.String>();
            appDomain.DelegateManager.RegisterMethodDelegate<UnityEngine.Collision>();
            appDomain.DelegateManager.RegisterMethodDelegate<UnityEngine.Collision2D>();
            appDomain.DelegateManager.RegisterMethodDelegate<UnityEngine.Collider>();
            appDomain.DelegateManager.RegisterMethodDelegate<UnityEngine.Collider2D>();
            appDomain.DelegateManager.RegisterMethodDelegate<UnityEngine.GameObject>();



            appDomain.DelegateManager.RegisterFunctionDelegate<System.Threading.Tasks.Task>();




            appDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((act) =>
            {
                return new UnityEngine.Events.UnityAction(() =>
                {
                    ((Action)act)();
                });
            });
            appDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<bool>>((act) =>
            {
                return new UnityEngine.Events.UnityAction<bool>((x) =>
                {
                    ((Action<bool>)act)(x);
                });
            });
            appDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<System.Int32>>((act) =>
             {
                 return new UnityEngine.Events.UnityAction<System.Int32>((arg0) =>
                 {
                     ((Action<System.Int32>)act)(arg0);
                 });
             });
            appDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<System.Single>>((act) =>
            {
                return new UnityEngine.Events.UnityAction<System.Single>((arg0) =>
                {
                    ((Action<float>)act)(arg0);
                });
            });
            appDomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction<string>>((act) =>
            {
                return new UnityEngine.Events.UnityAction<string>((x) =>
                {
                    ((Action<string>)act)(x);
                });
            });

        }
        // 注册跨域继承
        private void CrossBindingAdaptor()
        {
            appDomain.RegisterCrossBindingAdaptor(new AsyncStateMachineAdapter());
            appDomain.RegisterCrossBindingAdaptor(new CoroutineAdapter());
            appDomain.RegisterCrossBindingAdaptor(new MonoBehaviourAdapter());
        }
        void OnHotFixLoaded()
        {
            // 预先获得IMethod，可以减低每次调用查找方法耗用的时间
            IType type = appDomain.LoadedTypes["Hotfix.Hotfix"];
            // 实例化热更里的类
            hotfix = ((ILType)type).Instantiate(new object[] { gameObject });
            // 根据方法名称获取方法
            iAwake = type.GetMethod("Awake", 0);
            iStart = type.GetMethod("Start", 0);
            iEnable = type.GetMethod("OnEnable", 0);
            iDisable = type.GetMethod("OnDisable", 0);
            iUpdate = type.GetMethod("Update", 0);
            iLateUpdate = type.GetMethod("LateUpdate", 0);
            iFixedUpdate = type.GetMethod("FixedUpdate", 0);
            iOnDestroy = type.GetMethod("OnDestroy", 0);
        }
        private void Awake()
        {
            LoadHotFixAssembly();
            CallFunc(iAwake);
        }
        private void Start()
        {
            CallFunc(iStart);
        }
        private void OnEnable()
        {
            CallFunc(iEnable);
        }
        private void OnDisable()
        {
            CallFunc(iDisable);
        }
        private void Update()
        {
            CallFunc(iUpdate);
        }
        private void LateUpdate()
        {
            CallFunc(iLateUpdate);
        }
        private void FixedUpdate()
        {
            CallFunc(iFixedUpdate);
        }
        private void OnDestroy()
        {
            CallFunc(iOnDestroy);
            // 释放
            if (fs != null)
                fs.Close();
            if (p != null)
                p.Close();
            fs = null;
            p = null;
            appDomain?.Dispose();
        }
        private void CallFunc(IMethod method)
        {
            //using (var ctx = appdomain.BeginInvoke(method))
            //{
            //    ctx.PushObject(hotfix);
            //    ctx.Invoke();
            //}
            appDomain.Invoke(method, hotfix);
        }
    }
}